package com.ndp.fb.mq.consumer.business.syncbusiness;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ndp.fb.exception.ErrorCode;
import com.ndp.fb.exception.VideoUploadException;
import com.ndp.fb.fbagent.service.AdResouceFBService;
import com.ndp.fb.model.business.video.PreVideoUploadProgress;
import com.ndp.fb.model.fbagent.param.APIPriority;
import com.ndp.fb.model.fbagent.param.ApiCallBackTarget;
import com.ndp.fb.model.fbagent.param.advideo.AdVideoParam;
import com.ndp.fb.mongo.api.AdVideoMongoService;
import com.ndp.fb.mongo.model.AdvideoForMongo;
import com.ndp.fb.mongo.model.AdvideoSplit;
import com.ndp.fb.rdb.api.FbUserPermissionRdbService;
import com.ndp.fb.redis.api.RedisClient;
import com.ndp.fb.util.DateFormatUtil;

/**
 *
 * 异步分块上传视频到Facebook ，并更新 账号和视频id 的对应关系
 * @author hunk.qin
 */
@Component
public class SyncAdVideoBusiness {
    public static final String START_OFFSET = "start_offset";
    public static final String END_OFFSET = "end_offset";
    public static final String TOKEN = "token";
    public static final String ACCOUNT_ID = "accountId";
    public static final String UPLOAD_SESSION_ID = "upload_session_id";
    public static final String FILE_SIZE = "fileSize";
    public static final String MD_5 = "md5";
    public static final String SPLIT_INDEX = "splitIndex";
    public static final String UPLOAD_TRANSFER_TIME = "upload_transfer_time";
    public static final String UPLOAD_START_TIME = "upload_start_time";
    public static final String VIDEO_ID = "video_id";
    public static final String FB_VIDEO_ID = "fbVideoId";
    public static final String SUCCESS = "success";
    public static final String PROGRESSKEY = "progresskey";

    private final Logger logger = LoggerFactory.getLogger(getClass());
    @Reference
    private AdResouceFBService adResouceFBService;
    @Reference
    private FbUserPermissionRdbService fbUserPermissionRdbService;
    @Reference
    private  AdVideoMongoService adVideoMongoService;
    @Autowired
    private RedisClient redisClient;



    /**
     * 发起视频上传回调,
     *
     * @param body      eg. {"chunk_session_id": "10153169544814116", "video_id": "10153169544799116", "start_offset": "0", "end_offset": "1048576"}
     * @param equipment
     */
    public void startUploadAdVideoCallback(String body, Map<String, String> equipment) {

        String errormsg=null,videoMd5=null,token,accountId=null,progresskey="";
        boolean iserror=false;
        int currSplitIndex = 0;
        int fileSize,sumBlock=0;
        try {
            logger.info("发起视频上传请求回调开始" + JSON.toJSONString(equipment));
            token = equipment.get(TOKEN);
            accountId = equipment.get(ACCOUNT_ID);
            videoMd5=equipment.get(MD_5);
            progresskey=equipment.get(PROGRESSKEY);
            setUploadTransferTime(equipment, accountId, UPLOAD_START_TIME);
            fileSize = equipment.get(FILE_SIZE) == null ? 0 : Integer.parseInt(equipment.get(FILE_SIZE));

            JSONObject jsonObject = JSON.parseObject(body);
            String chunkSessionId = jsonObject.getString(UPLOAD_SESSION_ID);
            String videoId = jsonObject.getString(VIDEO_ID);
            String startOffset = jsonObject.getString(START_OFFSET);
            String endOffset = jsonObject.getString(END_OFFSET);
            if (StringUtils.isEmpty(chunkSessionId) || StringUtils.isEmpty(videoId) || StringUtils.isEmpty(startOffset) ||
                    StringUtils.isEmpty(endOffset)) {
                equipment.put(FB_VIDEO_ID, "0");
                logger.warn(accountId + " " + body);
                logger.warn(accountId + " chunk_session_id or video_id or start_offset or end_offset is null!");
                throw new VideoUploadException(ErrorCode.ERROR_20006,accountId,body);
            }
            equipment.put(FB_VIDEO_ID, videoId);
            equipment.put(UPLOAD_SESSION_ID, chunkSessionId);
            equipment.put(VIDEO_ID, videoId);
            equipment.put(startOffset, startOffset);

            equipment.put(SPLIT_INDEX, currSplitIndex + "");
            logger.info("accountId >>>> " + accountId + " videoMd5 >>>>" + videoMd5 + ", currSplitIndex >>>" + currSplitIndex + " 次");
            List<AdvideoForMongo> advideoForMongos = adVideoMongoService.findByMd5(videoMd5);
            List<AdvideoSplit> splits = advideoForMongos.get(0).getSplits();
            sumBlock=splits.size();
            AdvideoSplit adresourceSplit = splits.get(currSplitIndex);

            uploadBlock(Long.parseLong(accountId),token,fileSize,adresourceSplit,equipment,chunkSessionId);

        } catch (Exception e) {
            iserror=true;
            errormsg = setErrorMsg(e,"syncAdVideoCallback");
        } finally {
            PreVideoUploadProgress progress= (PreVideoUploadProgress) redisClient.get(progresskey);
            if(progress!=null){
                progress.setCountBlock(sumBlock);
                progress.setCurrentBlock(currSplitIndex);
                progress.setUpdateTime(DateFormatUtil.formatLong19(new Date()));
                setProgressFail(errormsg, iserror, progress);
                redisClient.set(progresskey,progress,1, TimeUnit.HOURS.toString());
            }else{
                logger.error("get video progress is null key : {}",progresskey);
            }

        }

    }


    public void transferUploadAdVideoCallback(String body, Map<String, String> equipment) {
        String errormsg=null,token,accountId=null ,uploadSessionId,videoMd5=null,progresskey="";
        boolean iserror=false;
        int fileSize;
        int currSplitIndex=0;
        try {
            logger.info("transfer视频上传请求回调开始" + JSON.toJSONString(equipment));
            progresskey=equipment.get(PROGRESSKEY);
            token = equipment.get(TOKEN);
            accountId = equipment.get(ACCOUNT_ID);
            uploadSessionId = equipment.get(UPLOAD_SESSION_ID);
            fileSize = Integer.parseInt(equipment.get(FILE_SIZE));
            videoMd5=equipment.get(MD_5);

            int splitIndex = Integer.parseInt(equipment.get(SPLIT_INDEX));
            setUploadTransferTime(equipment, accountId, UPLOAD_TRANSFER_TIME);

            JSONObject jsonObject = JSON.parseObject(body);
            String startOffset = jsonObject.getString(START_OFFSET);
            String endOffset = jsonObject.getString(END_OFFSET);
            if (StringUtils.isEmpty(startOffset) || StringUtils.isEmpty(endOffset)) {
                logger.warn(accountId + " " + body);
                logger.warn(accountId + " start_offset or end_offset is null!");
                throw new VideoUploadException(ErrorCode.ERROR_20007,accountId,body);
            }

            //本次上传splitIndex
            currSplitIndex = splitIndex + 1;
            equipment.put(SPLIT_INDEX, currSplitIndex + "");
            logger.info("accountVideoId >>>> " + accountId + " adVideoId >>>>" + videoMd5 + ", currSplitIndex >>>" + currSplitIndex + " 次");

            List<AdvideoForMongo> advideoForMongos = adVideoMongoService.findByMd5(videoMd5);
            List<AdvideoSplit> splits = advideoForMongos.get(0).getSplits();

            //相等说明上传完成,需要调用finish接口
            if (splits.size() == currSplitIndex) {
                logger.info("视频文件上传完毕!,需要发起finish请求" + JSON.toJSONString(equipment));
                equipment.put(START_OFFSET, startOffset);
                equipment.put(END_OFFSET, endOffset);
                fishUpload(equipment, token, accountId, uploadSessionId, fileSize);
            } else {
                logger.info("继续视频文件上传!" + JSON.toJSONString(equipment));
                AdvideoSplit adresourceSplit = splits.get(currSplitIndex);
                equipment.put(startOffset, startOffset);
                equipment.put(endOffset, endOffset);
                uploadBlock(Long.parseLong(accountId),token,fileSize, adresourceSplit,equipment,uploadSessionId);
            }
        } catch (Exception e) {
            iserror=true;
            errormsg = setErrorMsg(e,"syncAdVideoCallback");
        } finally {
            PreVideoUploadProgress progress= (PreVideoUploadProgress) redisClient.get(progresskey);
            if(progress!=null){
                progress.setCurrentBlock(currSplitIndex);
                progress.setUpdateTime(DateFormatUtil.formatLong19(new Date()));

                setProgressFail(errormsg, iserror, progress);
                redisClient.set(progresskey,progress,1, TimeUnit.HOURS.toString());
            }else{
                logger.error("get video progress is null key : {}",progresskey);
            }

        }


    }

    private void setProgressFail(String errormsg, boolean iserror, PreVideoUploadProgress progress) {
        if(!iserror){
            progress.setStatus(PreVideoUploadProgress.RUNING);
        }else{
            progress.setStatus(PreVideoUploadProgress.FAIL);
            progress.setErrormsg(errormsg);
            logger.error("set video progress fail",errormsg);
        }
    }

    private void fishUpload(Map<String, String> equipment, String token, String accountId, String uploadSessionId, int fileSize) throws Exception {
        AdVideoParam requestParam = new AdVideoParam();
        requestParam.setFbAccountId(Long.parseLong(accountId));
        requestParam.setAccessToken(token);
        requestParam.setFileSize(fileSize);
        requestParam.setApiPriorityCode(APIPriority.UploadVideoAsyn.getCode());
        requestParam.setUploadSessionId(uploadSessionId);
        requestParam.setEquipment(equipment);
        requestParam.setApiCallbackTargetType(ApiCallBackTarget.PublishResource.FinishUploadAdVideo.getTargetType());
        requestParam.setCallBack(true);
        adResouceFBService.finishUploadAdVideo(requestParam);
    }

    public void finishUploadAdVideoCallback(String body, Map<String, String> equipmentMap) {
        String errormsg=null,videoMd5=null,accountId=null,fbVideoId=null,progresskey="";
        boolean iserror=false;
        try {
            logger.info("finish视频上传请求回调开始" + JSON.toJSONString(equipmentMap));
            //解析FB返回值
            progresskey=equipmentMap.get(PROGRESSKEY);
            accountId = equipmentMap.get(ACCOUNT_ID);
            videoMd5= equipmentMap.get(MD_5);
            boolean isUploadSuccess = isUploadSuccess(body);
            if (!isUploadSuccess) {
                logger.error("finishUploadAdVideoCallback->上传视频失败->body->" + body);
                throw new VideoUploadException(ErrorCode.ERROR_20005,accountId,body);
            } else {
                 fbVideoId = equipmentMap.get(FB_VIDEO_ID);
                 adVideoMongoService.upateFbActVideoId(videoMd5,accountId,fbVideoId);
            }
            if (equipmentMap.get(UPLOAD_START_TIME) != null) {
                Long startTime = Long.parseLong(equipmentMap.get(UPLOAD_START_TIME));
                logger.info(JSON.toJSONString(equipmentMap) + " 整个耗时请求耗时:" + (System.currentTimeMillis() - startTime));
            }
        } catch (Exception e) {
            iserror=true;
            errormsg = setErrorMsg(e,"finishUploadAdVideoCallback");
        } finally {
            PreVideoUploadProgress progress= (PreVideoUploadProgress) redisClient.get(progresskey);
            if(progress!=null){
                progress.setUpdateTime(DateFormatUtil.formatLong19(new Date()));
                if(!iserror){
                    progress.setStatus(PreVideoUploadProgress.SUCCEED);
                    progress.setFbVideoId(fbVideoId);
                }else{
                    progress.setStatus(PreVideoUploadProgress.FAIL);
                    progress.setErrormsg(errormsg);
                    logger.error("set video progress fail",errormsg);
                }
                redisClient.set(progresskey,progress,1, TimeUnit.HOURS.toString());
            }else{
                logger.error("get video progress is null key : {}",progresskey);
            }

        }
    }

    private String setErrorMsg(Exception e,String mehtodName) {
        String errormsg;
        if(e!=null)
            errormsg=e.getMessage();
        else
           errormsg=mehtodName+ " null exception";
        logger.error("upload video error :"+mehtodName+ " error >>", e);
        logger.error(e.getMessage(), e);
        return  errormsg;
    }


    private boolean isUploadSuccess(String body) throws  Exception {
        boolean isUploadSuccess = false;
        if (StringUtils.isEmpty(body)) {
            return isUploadSuccess;
        }
        JSONObject jsonObject = JSON.parseObject(body);
        if (jsonObject.containsKey(SUCCESS)) {
            isUploadSuccess = jsonObject.getBoolean(SUCCESS);
        }
        return isUploadSuccess;
    }

    private void uploadBlock(Long accountId,String token,int fileSize,AdvideoSplit adresourceSplit,Map<String,String> equipment,String uploadSessionId ) throws  Exception{
        AdVideoParam requestParam = new AdVideoParam();
        requestParam.setUploadSessionId(uploadSessionId);
        requestParam.setFbAccountId(accountId);
        requestParam.setAccessToken(token);
        requestParam.setFileSize(fileSize);
        requestParam.setNfsId(adresourceSplit.getSplitNfsFileId());
        requestParam.setStartOffset(adresourceSplit.getStartPosition().intValue());
        requestParam.setEndOffset(adresourceSplit.getEndPosition().intValue());
        requestParam.setEquipment(equipment);
        requestParam.setApiPriorityCode(APIPriority.UploadVideoAsyn.getCode());
        requestParam.setApiCallbackTargetType(ApiCallBackTarget.PublishResource.TransferUploadAdVideo.getTargetType());
        requestParam.setCallBack(true);
        adResouceFBService.transferUploadAdVideo(requestParam);
    }

    private void setUploadTransferTime(Map<String, String> equipment, String accountId,String key) {
        if (equipment.get(key) != null) {
            Long startTime = Long.parseLong(equipment.get(key));
            logger.info(accountId + " start请求耗时:" + (System.currentTimeMillis() - startTime));
            equipment.put("upload_transfer_time:", String.valueOf(System.currentTimeMillis()));
        }
    }

}
